#ifndef _Channel_h
#define _Channel_h
#include <functional>
#include <memory>
#include <stdio.h>
#include <stdlib.h>
#include <string>
#include <sys/epoll.h>
#include <sys/types.h>
#include <unistd.h>

/*
这个channel类封装了一个文件描述符及其感兴趣的事件，同时channel类还提供了设置该fd的感兴趣的事件，以及将该fd及其
感兴趣的事件注册到事件监听器或从事件监听器移除，以及保存了该fd的每种事件对应的处理函数。
每个 Channel 对象只属于⼀个 EventLoop ，即只属于⼀个 IO 线程。只负责⼀个⽂件描述符（fd）的 IO 时间分
发，但不拥有这个 fd。 Channel 把不同的 IO 事件分发为不同的回调，回调⽤ C++11 的特性 function 表示。声
明周期由拥有它的类负责。

Channel 类的主要功能是：
1. 保存一个文件描述符 fd ，以及 fd 对应的事件类型 events 和 revents ，以及上一次的事件类型 lastEvents 。
2. 设置和获取 fd 对应的事件类型。
3. 获取 fd 对应的文件描述符。
4. 设置和获取 fd 对应的就绪事件。
5. 设置和获取事件的类型。
6. 设置和获取上一次的事件类型。
7. 设置当发生就绪事件时，根据事件类型调用相应的回调函数。
8. 设置对应事件的回调函数。
9. 判断当前事件和上一次事件是否相同。如果相同则不需要调用 epoll_mod 。
*/

namespace droneswarm
{
    class EventLoop;
    class DdapData;
    class Channel
    {
    private:
        typedef std::function< void() > Callback;
        typedef std::function< void( int fd, int err_num, std::string err_msg ) > ErrorCallback;
        EventLoop *loop_;     // Channel所属的EventLoop
        int fd_;              // Channel负责的文件描述符
        __uint32_t events_;   // Channel正在监听的事件
        __uint32_t revents_;  //返回的就绪事件
        __uint32_t lastEvents_;  //上一此事件（主要用于记录如果本次事件和上次事件一样，就没有必要调用epoll_mod了）
        int err_num;         //错误号
        std::string error_;  //错误信息
        //方便找到上一层持有该channel的对象
        std::weak_ptr< DdapData > holder_;
        // std::weak_ptr是为了解决智能指针的循环引用问题，它引用时不会将引用计数器加1.
    private:
        // int parse_URI();
        // int parse_Headers();
        // int analysisRequest();
        Callback readHandler_;        //读事件的回调函数
        Callback writeHandler_;       //写事件的回调函数
        ErrorCallback errorHandler_;  //错误事件的回调函数
        Callback connHandler_;        //连接事件的回调函数
    public:
        Channel( EventLoop *loop );  //默认构造
        Channel( EventLoop *loop, int fd );
        ~Channel();  //析构函数
        // setHolder函数用于设置当前channel是被哪个httpData对象所持有。
        void setHolder( std::shared_ptr< DdapData > holder ) { holder_ = holder; }
        std::shared_ptr< DdapData > getHolder()
        {
            std::shared_ptr< DdapData > ret( holder_.lock() );
            return ret;
        }
        //设置channel类感兴趣的回调函数,使用右值引用可以优化性能，因为其可以使用移动语义或完美转发，而不是复制一份数据
        void setReadHandler( Callback &&readHandler ) { readHandler_ = std::move( readHandler ); }
        void setWriteHandler( Callback &&writeHandler ) { writeHandler_ = std::move( writeHandler ); }
        void setErrorHandler( ErrorCallback &&errorHandler ) { errorHandler_ = std::move( errorHandler ); }
        void setConnHandler( Callback &&connHandler ) { connHandler_ = std::move( connHandler ); }

        // IO事件回调函数的调用接口
        // EventLoop中调用Loop开始事件循环，会调用poll得到就绪事件
        /**
         * @brief 使用 epoll 机制处理套接字事件。
         *
         * 该函数处理通过 epoll 事件循环检测到的各种套接字相关事件。它根据发生在套接字上的事件类型，
         * 管理响应动作，包括读取数据、写入数据、处理连接状态和管理错误。
         *
         * @details
         * 函数首先检查挂起事件（EPOLLHUP）而无读取事件，这表示连接已被另一端关闭，如果没有其他需要的操作，
         * 则提前返回。
         *
         * 接下来检查错误事件（EPOLLERR），如果存在，则调用注册的错误处理回调函数来处理套接字错误。
         *
         * 可读事件（EPOLLIN、EPOLLPRI、EPOLLRDHUP）通过调用 handleRead() 函数进行处理，该函数负责处理
         * 与从套接字读取数据相关的所有必要操作，包括处理传入数据或处理读半关闭情况。
         *
         * 如果套接字准备好写入（EPOLLOUT），则调用 handleWrite() 函数来发送可能排队等待传输的数据。
         *
         * 最后，无论发生了什么特定事件，都调用 handleConn() 函数，执行对套接字状态的任何必要的最终调整或
         * 重新配置事件监听参数。
         *
         * 使用此函数要求将其注册到监视指定事件的套接字的 epoll 事件循环中。
         *
         * @note 本函数假设在单线程上下文中调用，或者如果在多线程环境中使用，适当的同步处理应由外部处理。
         * 简单的说这个函数就是处理套接字事件的函数。
         */
        void HandleEvent()
        {
            //首先初始化event_s，处理的是当前事件，因此处理之前需要把其置为0，一次事件只使用一次。
            //如果后续还需要进行某种操作就重新设置。
            events_ = 0;
            //如果发生了挂起事件，但是没有发生读事件
            if ( ( revents_ & EPOLLHUP ) && !( revents_ & EPOLLIN ) )
            {
                events_ = 0;
                return;
            }
            //如果发生了错误事件
            if ( revents_ & EPOLLERR )
            {
                //如果有错误事件的回调函数，就调用错误事件的回调函数
                if ( errorHandler_ )
                    HandleError( fd_, err_num, error_ );
                events_ = 0;
                return;
            }
            //如果发生了读事件
            if ( revents_ & EPOLLIN )
            {
                HandleRead();
            }
            //如果发生了写事件
            if ( revents_ & EPOLLOUT )
            {
                HandleWrite();
            }
            //最后，无论是否发生了特定的读或写事件，都调用连接事件的回调函数
            HandleConn();
        }
        //处理读事件的回调函数
        void HandleRead();
        //处理写事件的回调函数
        void HandleWrite();
        //处理错误事件的回调函数
        void HandleError( int fd, int err_num, std::string short_msg );
        //处理连接事件的回调函数
        void HandleConn();
        //获取当前Channel的文件描述符
        int get_fd();
        //设置当前Channel的文件描述符
        void set_fd( int fd );

        //设置当前Channel的就绪事件
        void set_Revents( __uint32_t ev ) { revents_ = ev; }
        //设置当前Channel的事件类型
        void set_Events( __uint32_t ev ) { events_ = ev; }

        //设置当前Channel的上一次事件
        void set_LastEvents( __uint32_t ev ) { lastEvents_ = ev; }
        //获取当前channel的事件类型
        __uint32_t &getEvents() { return events_; }

        //判断上次事件和当前事件是否相同,不管相不相同都更新lastEvents_
        bool EqualAndUpdateLastEvents()
        {
            bool ret = ( lastEvents_ == events_ );
            lastEvents_ = events_;
            return ret;
        }

        //获取当前Channel的上一次事件
        __uint32_t getLastEvents() { return lastEvents_; }

    private:
        Channel( const Channel & ) = delete;
        Channel( const Channel && ) = delete;
        Channel &operator=( const Channel & ) = delete;
    };

    //创建一个指向Channel的智能指针，并重命名为SP_Channel
    typedef std::shared_ptr< Channel > SP_Channel;
}  // namespace droneswarm
#endif